/*
 * 
 *  This file is part of Sourceprojects.org Lycia Copyright 2010 noctarius
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 * 
 */
package org.sourceprojects.lycia.fluent;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

import org.sourceprojects.lycia.TypeConverter;
import org.sourceprojects.lycia.EndTagAware;
import org.sourceprojects.lycia.Generator;
import org.sourceprojects.lycia.GeneratorFactory;
import org.sourceprojects.lycia.LyciaConfigurator;
import org.sourceprojects.lycia.LyciaReaderType;
import org.sourceprojects.lycia.LyciaParser;
import org.sourceprojects.lycia.ProgressListener;
import org.sourceprojects.lycia.ResourceResolver;
import org.sourceprojects.lycia.GenerationStrategy;
import org.sourceprojects.lycia.exceptions.LyciaInitializationException;
import org.sourceprojects.lycia.internal.io.LyciaXmlReader;
import org.sourceprojects.lycia.internal.io.VisualAgeRPGTextReader;
import org.sourceprojects.lycia.internal.privileged.SetAccessible;
import org.sourceprojects.lycia.io.LyciaReader;
import org.xml.sax.ContentHandler;
import org.xml.sax.ErrorHandler;

@SuppressWarnings("unchecked")
class ConfigurationBuilderHelper {
	private ConfigurationBuilderHelper() {
	}

	static <B> ConfigurationBuilder<B> createIgnoreCommentsConfigurator(
			final boolean ignore) {

		return new AbstractConfigurationBuilder<B>() {
			@Override
			public void configure(LyciaParser<B> xmlParser) {
				xmlParser.getConfigurator().setIgnoringComments(ignore);
			}
		};
	}

	static <B> ConfigurationBuilder<B> createEndTagReachedListener(
			final EndTagAware<B> listener) {

		return new AbstractConfigurationBuilder<B>() {
			@Override
			public void configure(LyciaParser<B> xmlParser) {
				xmlParser.getConfigurator().addEndTagReachedListener(listener);
			}
		};
	}

	static <B> ConfigurationBuilder<B> createValidatingSchema(
			final boolean validatingSchema) {

		return new AbstractConfigurationBuilder<B>() {
			@Override
			public void configure(final LyciaParser<B> xmlParser) {
				xmlParser.setValidatingSchema(validatingSchema);
			}
		};
	}

	static <B> ConfigurationBuilder<B> createGenerationStrategy(
			final GenerationStrategy generationStrategy,
			final Class<? extends Generator> generatorClass) {

		if (generationStrategy == null) {
			throw new IllegalArgumentException(
					"GenerationStrategy cannot be null");
		}

		if (generationStrategy == GenerationStrategy.Customized
				&& generatorClass == null) {
			throw new IllegalArgumentException(
					"When using GenerationStrategy.Customized Generator class cannot be null");
		}

		return new AbstractConfigurationBuilder<B>() {
			@Override
			public int getPriority() {
				return Integer.MIN_VALUE + 1;
			}

			@Override
			public void configure(final LyciaParser<B> xmlParser) {
				final LyciaConfigurator<B> configurator = xmlParser
						.getConfigurator();

				configurator.setGenerationStrategy(generationStrategy);
				if (generationStrategy == GenerationStrategy.Customized) {
					configurator.getGeneratorFactory().setCustomGeneratorClass(
							(Class<? extends Generator<B>>) generatorClass);
				}
			}
		};
	}

	static <B> ConfigurationBuilder<B> createXmlParserConfigurator(
			final Class<? extends LyciaConfigurator> configuratorClass) {

		if (configuratorClass == null) {
			throw new IllegalArgumentException(
					"XmlParserConfigurator class cannot be null");
		}

		return new AbstractConfigurationBuilder<B>() {
			@Override
			public int getPriority() {
				return Integer.MIN_VALUE;
			}

			@Override
			public void configure(final LyciaParser<B> xmlParser) {
				try {
					Constructor<? extends LyciaConfigurator<B>> constructor;

					final Class<? extends LyciaConfigurator<B>> clazz = (Class<? extends LyciaConfigurator<B>>) configuratorClass;
					constructor = clazz.getConstructor(LyciaParser.class,
							LyciaConfigurator.class);

					final LyciaConfigurator<B> configurator = constructor
							.newInstance(xmlParser, xmlParser.getConfigurator());

					xmlParser.setConfigurator(configurator);
				} catch (final InvocationTargetException e) {
					throw new LyciaInitializationException(e);
				} catch (final IllegalArgumentException e) {
					throw new LyciaInitializationException(e);
				} catch (final InstantiationException e) {
					throw new LyciaInitializationException(e);
				} catch (final IllegalAccessException e) {
					throw new LyciaInitializationException(e);
				} catch (final SecurityException e) {
					throw new LyciaInitializationException(e);
				} catch (final NoSuchMethodException e) {
					throw new LyciaInitializationException(e);
				}
			}
		};
	}

	static <B> ConfigurationBuilder<B> createContentHandler(
			final ContentHandler... contentHandlers) {

		return new AbstractConfigurationBuilder<B>() {
			@Override
			public void configure(final LyciaParser<B> xmlParser) {
				for (final ContentHandler contentHandler : contentHandlers) {
					xmlParser.getConfigurator().addContentHandler(
							contentHandler);
				}
			}
		};
	}

	static <B> ConfigurationBuilder<B> createDoNothingConfigurator() {
		return new AbstractConfigurationBuilder<B>() {
			@Override
			public void configure(final LyciaParser<B> xmlParser) {
				// just do nothing
			}
		};
	}

	static <B> ConfigurationBuilder<B> createContextObjectConfigurator(
			final Class<?> contextObjectClass) {

		try {

			Constructor<B> constructor;
			constructor = (Constructor<B>) contextObjectClass.getConstructor();

			if (!constructor.isAccessible())
				SetAccessible.setAccessible(constructor);

			final B contextObject = constructor.newInstance();

			return new AbstractConfigurationBuilder<B>() {
				@Override
				public void configure(final LyciaParser<B> xmlParser) {
					xmlParser.getParserContext()
							.setContextObject(contextObject);
				}
			};
		} catch (final InstantiationException e) {
			throw new LyciaInitializationException(e);
		} catch (final IllegalAccessException e) {
			throw new LyciaInitializationException(e);
		} catch (SecurityException e) {
			throw new LyciaInitializationException(e);
		} catch (NoSuchMethodException e) {
			throw new LyciaInitializationException(e);
		} catch (IllegalArgumentException e) {
			throw new LyciaInitializationException(e);
		} catch (InvocationTargetException e) {
			throw new LyciaInitializationException(e);
		}
	}

	static <B> ConfigurationBuilder<B> createContextObjectConfigurator(
			final B contextObject) {

		return new AbstractConfigurationBuilder<B>() {
			@Override
			public void configure(final LyciaParser<B> xmlParser) {
				xmlParser.getParserContext().setContextObject(contextObject);
			}
		};
	}

	static <B> ConfigurationBuilder<B> createResourceResolverConfigurator(
			final ResourceResolver resourceResolver) {

		return new AbstractConfigurationBuilder<B>() {
			@Override
			public void configure(final LyciaParser<B> xmlParser) {
				xmlParser.setResourceResolver(resourceResolver);
			}
		};
	}

	static <B> ConfigurationBuilder<B> createErrorHandlerConfigurator(
			final ErrorHandler errorHandler) {

		return new AbstractConfigurationBuilder<B>() {
			@Override
			public void configure(final LyciaParser<B> xmlParser) {
				xmlParser.setErrorHandler(errorHandler);
			}
		};
	}

	static <B> ConfigurationBuilder<B> createProgressListenerConfigurator(
			final ProgressListener... progressListeners) {

		return new AbstractConfigurationBuilder<B>() {
			@Override
			public void configure(final LyciaParser<B> xmlParser) {
				for (final ProgressListener progressListener : progressListeners) {
					xmlParser.addProgressListener(progressListener);
				}
			}
		};
	}

	static <B> ConfigurationBuilder<B> createLyciaReaderConfigurator(
			final Class<? extends LyciaReader> lyciaReaderClass) {

		return new AbstractConfigurationBuilder<B>() {
			@Override
			public void configure(final LyciaParser<B> xmlParser) {
				Class<? extends LyciaReader<B>> clazz = (Class<? extends LyciaReader<B>>) lyciaReaderClass;
				xmlParser.setReader(clazz);
			}
		};
	}

	static <B> ConfigurationBuilder<B> createLyciaReaderConfigurator(
			final LyciaReaderType readerType) {

		return new AbstractConfigurationBuilder<B>() {
			@Override
			public void configure(final LyciaParser<B> xmlParser) {
				Class<? extends LyciaReader<B>> clazz = getReaderClass(readerType);
				xmlParser.setReader(clazz);
			}

			private Class<? extends LyciaReader<B>> getReaderClass(
					LyciaReaderType readerType) {

				Class<? extends LyciaReader> clazz;
				switch (readerType) {
				case VisualAgeRPG:
					clazz = VisualAgeRPGTextReader.class;
					break;

				case Xml:
				default:
					clazz = LyciaXmlReader.class;
				}

				return (Class<? extends LyciaReader<B>>) clazz;
			}
		};
	}

	static <B> ConfigurationBuilder<B> createCacheExpirationTimeConfigurator(
			final long expirationTime) {

		return new AbstractConfigurationBuilder<B>() {
			@Override
			public void configure(final LyciaParser<B> xmlParser) {
				xmlParser.setCacheExpirationTime(expirationTime);
			}
		};
	}

	static <B> ConfigurationBuilder<B> createGeneratorFactoryConfigurator(
			final GeneratorFactory<B> generatorFactory) {

		return new AbstractConfigurationBuilder<B>() {

			@Override
			public void configure(final LyciaParser<B> xmlParser) {
				xmlParser.getConfigurator().setGeneratorFactory(
						generatorFactory);
			}
		};
	}

	static <B> ConfigurationBuilder createNullAwareConfigurator(
			final boolean nullAware) {

		return new AbstractConfigurationBuilder<B>() {

			@Override
			public void configure(LyciaParser<B> xmlParser) {
				xmlParser.getConfigurator().setNullAware(nullAware);
			}
		};
	}

	static <B> ConfigurationBuilder createDataConverter(
			final TypeConverter... dataConverters) {

		return new AbstractConfigurationBuilder<B>() {

			@Override
			public void configure(LyciaParser<B> xmlParser) {
				for (TypeConverter dataConverter : dataConverters) {
					xmlParser.getConfigurator().addGlobalDataConverter(
							dataConverter);
				}
			}
		};
	}

	static abstract class AbstractConfigurationBuilder<B> implements
			ConfigurationBuilder<B> {

		public int getPriority() {
			return 0;
		}
	}

}
